<?php

defined('__MAPEXEC__') or die('No direct access allowed');

abstract class Maps_Geocode_Common {
  
  private $apiKey = '';
  private $db = null;
  private $cache = null;
  private $encoding = 'utf8';
  private $url = '';
  
  /**
   * Class constructor. You must call Maps_Geocode_Cache::initialize()
   * before instantiating this class.
   * @param   string  Map service specific API key
   * @return  void
   */
  public function __construct($apiKey='') {
    $this->setAPIKey($apiKey);
    $this->db = Maps_Geocode_DB::instance();
    $this->cache = Maps_Geocode_Cache::instance();
  }
  
  /**
   * Class destructor.
   * @param   void
   * @return  void
   */
  public function __destroy() {
    $this->db = null;
    $this->cache = null;
  }
  
  /**
   * Batch geocode multiple addresses. Useful for when you have a database full
   * of addresses and want to create a batch import process for integrating
   * geographic data into your application.
   * @param   array   Array of fully qualified addresses: Address, City, Region, Country
   * @return  array   Array of Maps_LatLng objects
   */
  public function batchGetCoordsByAddress($addresses) {
    $results = array();
    foreach ($addresses as $address) {
      $results[] = $this->getCoordsByAddress($address);
    }
    return $results;
  }
  
  /**
   * Creates a location object
   * @param   String  Fully qualified address, or street number and name
   * @param   String  City
   * @param   String  Region, province or state
   * @param   String  Postal or zip code
   * @param   String  Country code, e.g. US for United States of America.
   * @return  Maps_Geocode_Location   Location object
   */
  protected function createLocation($address, $city=null, $region=null, $postalCode=null, $country=null) {
    return new Maps_Geocode_Location($address, $city, $region, $postalCode, $country);
  }
  
  /**
   * Abstract function implemented by child classes
   */
  abstract protected function extractCoords($geoData);
  
  /**
   * Gets the API key used in geo coding queries
   * @param   void
   * @return  string  Map service specific API key
   */
  protected function getAPIKey() {
    return $this->apiKey;
  }
  
  /**
   * Gets geographic coordinates for an address.
   * @param   string  Fully qualified street address
   * @return  array   Array of latitude and longitude coordinates.
   */
  public function getCoordsByAddress($address, $city=null, $region=null, $postalCode=null, $country=null) {
    $location = $this->createLocation($address, $city, $region, $postalCode, $country);
    $address = $location->getFullyQualifiedAddress();
    $coords = $this->cache->get($address);
    
    if ( empty($coords) ) {
      $coords = $this->db->fetch($address);
    }
    
    if ( empty($coords) ) {
      $coords = $this->lookup($location);
      d($coords, '$coords');
      if ( !empty($coords) ) {
        $this->cache->insert($address, $coords);
        $this->db->insert($address, $coords);
      }
    }
    
    return $coords;
  }
  
  /**
   * Gets the distance between two latitude and longitude points.
   * @param   Maps_LatLng   Latitude and longitude object for point A
   * @param   Maps_LatLng   Latitude and longitude object for point B
   * @param   String        Unit of measure for distance. Default is miles
   * @return  Float         Unit of measure for distance.
   */
  public function getDistance(Maps_LatLng $a, Maps_LatLng $b, $unit='M') {
    $distance = 69.09 * rad2deg(acos(sin(deg2rad($a->getLat())) * sin(deg2rad($b->getLat())) +  cos(deg2rad($a->getLat())) * cos(deg2rad($b->getLat())) * cos(deg2rad($a->getLng() - $b->getLng()))));
    
    switch(strtoupper($unit))
    {
      case 'K':
        // kilometers
        $distance = $distance * 1.609344;
        break;
      case 'N':
        // nautical miles
        $distance = $distance * 0.868976242;
        break;
      case 'F':
        // feet
        $distance = $distance * 5280;
        break;
      case 'I':
        // inches
        $distance = $distance * 63360;
        break;
    }
    
    return $distance;
  }
  
  /**
   * Gets the lookup format being used.
   * @param   void
   * @return  string  Lookup format
   */
  public function getFormat() {
    return $this->format;
  }
  
  /**
   * Returns the character encoding used.
   * @param   void
   * @return  String  Character encoding
   */
  protected function getEncoding() {
    return $this->encoding;
  }
  
  /**
   * Abstract function implemented by child classes.
   */
  abstract protected function lookup(Maps_Geocode_Location $location);
  
  /**
   * Sets the API key used for geocoding queries.
   * @param   string  Map service specific API key
   * @return  void
   */
  protected function setAPIKey($str) {
    if ( is_string($str) && !empty($str) ) {
      $this->apiKey = $str;
    }
  }
  
  /**
   * Sets the lookup format being used.
   * @param   string  Lookup format
   * @return  void
   */
  public function setFormat($format='') {
    if ( !empty($format) ) {
      $this->format = (string) $format;
    }
  }
  
}

?>